<?php
declare(strict_types=1);

namespace SixShop\TencentCloudSms;

use TencentCloud\Common\Credential;
use TencentCloud\Common\Exception\TencentCloudSDKException;
use TencentCloud\Common\Profile\ClientProfile;
use TencentCloud\Common\Profile\HttpProfile;
use TencentCloud\Sms\V20210111\Models\SendSmsRequest;
use TencentCloud\Sms\V20210111\SmsClient as TencentCloudSmsClient;

class SmsClient
{
    private array $templateMap = [];

    private TencentCloudSmsClient $client;

    private array $errorMesageMap = [
        'FailedOperation.ContainSensitiveWord' => '包含敏感词',
        'FailedOperation.InsufficientBalanceInSmsPackage' => '套餐包余量不足',
        'FailedOperation.PhoneNumberInBlacklist' => '号码在黑名单中',
        'FailedOperation.SignatureIncorrectOrUnapproved' => '签名未审核通过',
        'FailedOperation.TemplateIncorrectOrUnapproved' => '模板未审核通过',
        'FailedOperation.TemplateUnapprovedOrNotExist' => '模板未审核通过或不存在',
        'InvalidParameterValue.ContentLengthLimit' => '内容长度超出限制',
        'InvalidParameterValue.IncorrectPhoneNumber' => '号码格式错误',
        'LimitExceeded.PhoneNumberDailyLimit' => '您的手机号发送次数超出单日限制'
    ];

    public function __construct(private Config $config)
    {
        $this->templateMap = array_to_map($this->config->tencent_sms_templates, 'template_id', 'template_content');
        $cred = new Credential($this->config->secret_id, $this->config->secret_key);
        $httpProfile = new HttpProfile();
        $httpProfile->setEndpoint('sms.tencentcloudapi.com');
        $clientProfile = new ClientProfile();
        $clientProfile->setHttpProfile($httpProfile);
        $this->client = new TencentCloudSmsClient($cred, 'ap-guangzhou', $clientProfile);
    }

    /**
     * 发送短信
     *
     * @param array|string $phoneNumberSet 手机号码列表
     * @param string $templateId 模板ID
     * @param array|string $templateParamSet 模板参数
     * @param callable|null $next 中间件回调函数
     * @param bool $autoCheckError 是否自动检查错误
     * @return array
     */
    public function sendSms(
        array|string $phoneNumberSet, 
        string $templateId, 
        array|string $templateParamSet, 
        ?callable $next = null,
        bool $autoCheckError = true
    ): array {
        $phoneNumberSet = (array)$phoneNumberSet;
        $templateParamSet = (array)$templateParamSet;
        
        if (!isset($this->templateMap[$templateId])) {
            throw new \InvalidArgumentException("template_id {$templateId} not found");
        }
        
        $req = $this->getSendSmsRequest($phoneNumberSet, $templateId, $templateParamSet);
        
        // 默认的发送处理函数
        $sendHandler = function($request) {
            try {
                $resp = $this->client->SendSms($request);
                return json_decode($resp->toJsonString(), true);
            } catch (TencentCloudSDKException $e) {
                throw new \RuntimeException($e->getMessage(), $e->getCode(), $e);
            }
        };
        
        // 如果没有中间件，直接发送
        if ($next === null) {
            $response = $sendHandler($req);
            if ($autoCheckError) {
                $this->checkError($response);
            }
            return $response;
        }
        
        // 包装中间件调用
        $middleware = function($request, $checkError = true) use ($sendHandler, $autoCheckError) {
            $response = $sendHandler($request);
            // 只有当自动检查错误开启且中间件未显式禁用检查时才进行检查
            if ($autoCheckError && $checkError !== false) {
                $this->checkError($response);
            }
            return $response;
        };
        
        // 执行中间件链
        $response = $next($req, $middleware);
        
        // 确保返回的是数组
        return is_array($response) ? $response : [];
    }

    private function getSendSmsRequest(array $phoneNumberSet, string $templateId, array $templateParamSet): SendSmsRequest
    {
        $req = new SendSmsRequest();
        $req->setPhoneNumberSet($phoneNumberSet);
        $req->setSmsSdkAppId($this->config->app_id);
        $req->setTemplateId($templateId);
        $req->setSignName($this->config->sign_name);
        $req->setTemplateParamSet($templateParamSet);
        return $req;
    }

    private function checkError(array $resp): void
    {
        foreach ($resp['SendStatusSet'] as $statusItem) {
            if ($statusItem['Code'] !== 'Ok') {
                throw new \RuntimeException($this->errorMesageMap[$statusItem['Code']] ?? $statusItem['Message']);
            }
        }
    }
}